home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Applications / NIH Image 1.62b11 / src / Edm.p < prev    next >
Text File  |  1996-12-16  |  18KB  |  703 lines

  1. unit EDM;
  2.  
  3. {Euclidian distance maps, ultimate eroded points, and watershed segmentation}
  4.  
  5. interface
  6.  
  7.     uses
  8.         Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, 
  9.         Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows,
  10.         Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes,
  11.         globals, Utilities, Graphics, Analysis;
  12.  
  13.  
  14.     procedure MakeEDM(item: integer);
  15.  
  16.  
  17. implementation
  18.  
  19.     type
  20.         Image16Data = packed array[0..maxImageSize] of integer;
  21.         Image16Ptr = ^Image16Data;
  22.         StartsArray = array[0..255] of LongInt;
  23.  
  24.  
  25. function FindUtimatePoints(MaxEDM,item: LongInt; image2: ImageP; 
  26.         LevelStart: StartsArray; xCoordinate, yCoordinate: Image16Ptr): boolean;
  27. {
  28. Finds peaks in the EDM that contain pixels equal to or greater than
  29. all of their neighbors.
  30. }
  31. var
  32.     x, y, level, rowsize, offset, i, count: LongInt;
  33.     CoordOffset, xmax, ymax: LongInt;
  34.     NextTick: LongInt;
  35.     image: ImageP;
  36.     SetPixel: boolean;
  37. begin
  38.     with info^ do begin
  39.         image := ImageP(PicbaseAddr);
  40.         BlockMove(image, image2, PixMapSize);
  41.         rowsize := BytesPerRow;
  42.         xmax := PixelsPerLine - 1;
  43.         ymax := nLines - 1;
  44.         NextTick := TickCount + 15;
  45.         for level := maxEDM - 1 downto 1 do begin
  46.             repeat
  47.                 count := 0;
  48.                 for i := 0 to Histogram[level] - 1 do begin
  49.                     CoordOffset := LevelStart[level] + i;
  50.                       x:= xCoordinate^[CoordOffset];
  51.                       y:= yCoordinate^[CoordOffset];
  52.                     offset := x + y * rowsize;
  53.                     if image^[offset] <> 255 then begin
  54.                         SetPixel := false;
  55.                         if (x > 0) and (y > 0) then
  56.                             if image^[offset - rowSize - 1] > level then
  57.                                 SetPixel := true;
  58.                         if y > 0 then
  59.                             if image^[offset - rowSize] > level then
  60.                                 SetPixel := true;
  61.                         if (x < xmax) and (y > 0) then
  62.                             if image^[offset - rowSize + 1] > level then
  63.                                 SetPixel := true;
  64.                         if x < xmax then
  65.                             if image^[offset + 1] > level then
  66.                                 SetPixel := true;
  67.                         if (x < xmax) and (y < ymax) then
  68.                             if image^[offset + rowSize + 1] > level then
  69.                                 SetPixel := true;
  70.                         if y < ymax then
  71.                             if image^[offset + rowSize] > level then
  72.                                 SetPixel := true;
  73.                         if (x > 0) and (y < ymax) then
  74.                             if image^[offset + rowSize - 1] > level then
  75.                                 SetPixel := true;
  76.                         if x > 0 then
  77.                             if image^[offset - 1] > level then
  78.                                 SetPixel := true;
  79.                         if SetPixel then begin
  80.                             image^[offset] := 255;
  81.                             count := count + 1;
  82.                         end;
  83.                     end; {if}
  84.                 end; {for i}
  85.             until count = 0;
  86.             if TickCount > NextTick then begin
  87.                 ShowAnimatedWatch;
  88.                 if CommandPeriod then begin
  89.                     beep;
  90.                     undo;
  91.                     WhatToUndo := NothingToUndo;
  92.                     FindUtimatePoints := false;
  93.                     exit(FindUtimatePoints);
  94.                 end;
  95.                 NextTick := TickCount + 15;
  96.             end;
  97.         end; {for level}
  98.         if item = WatershedItem then begin
  99.             for i := 0 to info^.PixMapSize - 1 do
  100.                 if (image^[i] > 0) and (image^[i] < 255) then
  101.                     image2^[i] := 255;
  102.             BlockMove(image2, image, PixMapSize);
  103.         end else begin
  104.             for i := 0 to info^.PixMapSize - 1 do
  105.                 if image^[i] = 255 then
  106.                     image^[i] := 0;
  107.         end;
  108.         FindUtimatePoints := true;
  109.     end; {with}
  110. end;
  111.  
  112.  
  113. procedure DoWatershedSegmentation(MaxEDM: LongInt; image2: ImageP; 
  114.           LevelStart: StartsArray; xCoordinate, yCoordinate: Image16Ptr;
  115.           var iterations: LongInt);
  116. {
  117. Assumes the current image contains an EDM and that the peaks in the EDM
  118. (the ultimate eroded points) have been set to 255. The EDM is dilated from
  119. these peaks, starting at the highest peak and working down. At each level,
  120. the dilation is constrained to pixels whose values are at that level and
  121. also constrained to prevent features from merging.
  122. }
  123. var
  124.     rowSize, NextTicks: LongInt;
  125.     level, x, y, offset, xmax, ymax: LongInt;
  126.     count, FirstCount: LongInt;
  127.     table: FateTable;
  128.     image: ImageP;
  129.  
  130.     procedure CheckAbort;
  131.     begin
  132.         UpdatePicWindow;
  133.         if CommandPeriod then begin
  134.             beep;
  135.             undo;
  136.             WhatToUndo := NothingToUndo;
  137.             exit(DoWatershedSegmentation);
  138.         end;
  139.         NextTicks := TickCount + 30;
  140.         if OptionKeyDown then
  141.             wait(30);
  142.     end;
  143.     
  144.     procedure MakeFateTable;
  145.     {Add pixel on 1st pass if bit 0 is set, on 2nd pass if bit 1 is}
  146.     {set, on 3rd pass if bit 2 is set, and on 4th pass if bit 3 is set.}
  147.     {E.g. '4' = add on 3rd pass, '3' = add on either 1st or 2nd pass,}
  148.     {'f' = add on any pass. There is a routine in 'user.p' that draws}
  149.     {all 256 neighborhoods.}
  150.     const
  151.         s999 = '01234567890123456789012345678901';
  152.         
  153.         s000 = '0044004480cc80cc0000000080cc80cc';
  154.         s032 = '1000000080ff80ff1000000090ff90ff';
  155.         s064 = '00000000000000000000000000000000';
  156.         s096 = '1000000090ff90ff1000000090ff90ff';
  157.         s128 = '2266006600ff00ff0000000000ff00ff';
  158.         s160 = '13ff00ffffffffff33ff00ffffffffff';
  159.         s192 = '2266006600ff00ff0000000000ff00ff';
  160.         s224 = '33ff00ffffffffff33ff00fffffffff';
  161.     var
  162.         s: str255;
  163.         c: char;
  164.         i: LongInt;
  165.     begin
  166.         s := concat(s000, s032, s064, s096, s128, s160, s192, s224);
  167.         for i := 0 to 254 do begin
  168.             c := s[i + 1];
  169.             if c <= '9' then
  170.                 table[i] := ord(c) - ord('0')
  171.             else
  172.                 table[i] := 10 + ord(c) - ord('a')
  173.         end;
  174.         table[255] := 15; {f}
  175.     end;
  176.  
  177.     procedure ProcessLevel(level, pass: LongInt);
  178.     var
  179.         index, x, y, i, CoordOffset, offset: LongInt;
  180.     begin
  181.         BlockMove(image, image2, info^.PixMapSize);
  182.         for i := 0 to Histogram[level] - 1 do begin
  183.             CoordOffset := LevelStart[level] + i;
  184.               x:= xCoordinate^[CoordOffset];
  185.               y:= yCoordinate^[CoordOffset];
  186.             offset := x + y * rowsize;
  187.             if image2^[offset] <> 255 then begin
  188.                 index := 0;
  189.                 if (x > 0) and (y > 0) then
  190.                     if image2^[offset - rowSize - 1] = 255 then
  191.                         index := bor(index, 1);
  192.                 if y > 0 then
  193.                     if image2^[offset - rowSize] = 255 then
  194.                         index := bor(index, 2);
  195.                 if (x < xmax) and (y > 0) then
  196.                     if image2^[offset - rowSize + 1] = 255 then
  197.                         index := bor(index, 4);
  198.                 if x < xmax then
  199.                     if image2^[offset + 1] = 255 then
  200.                         index := bor(index, 8);
  201.                 if (x < xmax) and (y < ymax) then
  202.                     if image2^[offset + rowSize + 1] = 255 then
  203.                         index := bor(index, 16);
  204.                 if y < ymax then
  205.                     if image2^[offset + rowSize] = 255 then
  206.                         index := bor(index, 32);
  207.                 if (x > 0) and (y < ymax) then
  208.                     if image2^[offset + rowSize - 1] = 255 then
  209.                         index := bor(index, 64);
  210.                 if x > 0 then
  211.                     if image2^[offset - 1] = 255 then
  212.                         index := bor(index, 128);
  213.                 case pass of
  214.                     1: if band(table[index], 1) = 1 then begin
  215.                             image^[offset] := 255;
  216.                             count := count + 1;
  217.                         end;
  218.                     2:  if band(table[index], 2) = 2 then begin
  219.                             image^[offset] := 255;
  220.                             count := count + 1;
  221.                         end;
  222.                     3:  if band(table[index], 4) = 4 then begin
  223.                             image^[offset] := 255;
  224.                             count := count + 1;
  225.                         end;
  226.                     4: if band(table[index], 8) = 8 then begin
  227.                             image^[offset] := 255;
  228.                             count := count + 1;
  229.                         end;
  230.                 end; {case}
  231.             end; {if}
  232.         end; {for i}
  233.     end; {ProcessLevel}
  234.  
  235.     procedure PostProcess;
  236.     var
  237.         i: LongInt;
  238.     begin
  239.         for i := 0 to info^.PixMapSize - 1 do
  240.             if image^[i] < 255 then
  241.                 image^[i] := 0
  242.     end;
  243.     
  244. begin
  245.     with info^ do begin
  246.         rowSize := BytesPerRow;
  247.         MakeFateTable;
  248.         image := ImageP(PicbaseAddr);
  249.         xmax := PixelsPerLine - 1;
  250.         ymax := nLines - 1;
  251.         NextTicks := TickCount;
  252.         iterations := 0;
  253.         for level := maxEDM - 1 downto 1 do begin
  254.             repeat
  255.                 count := 0;
  256.                 ProcessLevel(level, 1);
  257.                 ProcessLevel(level, 3);
  258.                 ProcessLevel(level, 2);
  259.                 ProcessLevel(level, 4);
  260.                 iterations := iterations + 1;
  261.             until count = 0;
  262.         if TickCount > NextTicks then
  263.             CheckAbort;
  264.       end; {for level}
  265.     PostProcess;
  266.     UpdatePicWindow;
  267.   end;
  268. end;
  269.  
  270.  
  271. procedure SmoothEDM(image2: ImageP);
  272. var
  273.     x, y, rowsize, offset, sum: LongInt;
  274.     xmax, ymax: LongInt;
  275.     image: ImageP;
  276. begin
  277.     with info^ do begin
  278.         image := ImageP(PicbaseAddr);
  279.         BlockMove(image, image2, PixMapSize);
  280.         rowsize := BytesPerRow;
  281.         xmax := PixelsPerLine - 1;
  282.         ymax := nLines - 1;
  283.         for y := 0 to nLines - 1 do begin
  284.             for x := 0 to PixelsPerLine - 1 do begin
  285.                 offset := x + y * rowsize;
  286.                 if image2^[offset] <> 1 then begin
  287.                     sum := image2^[offset] * 2;
  288.                     if (x > 0) and (y > 0) then
  289.                         sum := sum + image2^[offset - rowSize - 1];
  290.                     if y > 0 then
  291.                         sum := sum + image2^[offset - rowSize];
  292.                     if (x < xmax) and (y > 0) then
  293.                         sum := sum + image2^[offset - rowSize + 1];
  294.                     if x < xmax then
  295.                         sum := sum + image2^[offset + 1];
  296.                     if (x < xmax) and (y < ymax) then
  297.                         sum := sum + image2^[offset + rowSize + 1];
  298.                     if y < ymax then
  299.                         sum := sum + image2^[offset + rowSize];
  300.                     if (x > 0) and (y < ymax) then
  301.                         sum := sum + image2^[offset + rowSize - 1];
  302.                     if x > 0 then
  303.                         sum := sum + image2^[offset - 1];
  304.                     image^[offset] := sum div 10;
  305.                     end;
  306.             end; {for x}
  307.         end; {for y}
  308.     end; {with}
  309. end;
  310.  
  311.  
  312. procedure MakeEDM(item: integer);
  313. {
  314. Converts a binary image into a grayscale Euclidian distance
  315. map (EDM). Each foreground (black) pixel in the binary image
  316. is assigned a value equal to its distance from the nearest 
  317. background (white) pixel. Uses the two pass EDM algorithm
  318. from the "Image Pricessing Handbook" by John Russ.  
  319.  }
  320. const
  321.     one = 41;
  322.     sqrt2 = 58; {~41*sqrt(2)}
  323.     sqrt5 = 92; {~41*sqrt(5)}
  324.  
  325. var
  326.     x, y, xmax, ymax, i, MaxEDM: LongInt;
  327.     StartTicks, NextTicks, offset, rowsize: LongInt;
  328.     iterations: LongInt;
  329.     PointsOK: boolean;
  330.     image16: Image16Ptr;
  331.     image2: ImageP;
  332.     str: str255;
  333.     LevelStart: StartsArray;
  334.     xCoordinate, yCoordinate: Image16Ptr;
  335.     
  336.     function ConvertToIntegers:boolean;
  337.     var
  338.         x, y, offset: LongInt;
  339.     begin
  340.         with info^ do begin
  341.             image16 := Image16Ptr(NewPtr(rowsize * nLines * SizeOf(integer)));
  342.             if image16 = nil then begin
  343.                 ConvertToIntegers := false;
  344.                 exit(ConvertToIntegers);
  345.             end;
  346.             for y := 0 to nLines - 1 do
  347.               for x := 0 to PixelsPerLine - 1 do begin
  348.                 offset := x + y * rowsize;
  349.                     image16^[offset] := ImageP(PicBaseAddr)^[offset] * one;
  350.               end;
  351.             ConvertToIntegers := true;
  352.         end;
  353.     end;
  354.  
  355.     procedure ConvertToBytes;
  356.     var
  357.         x, y, v, round, offset: LongInt;
  358.     begin
  359.         round := one div 2;
  360.         MaxEDM := 0;
  361.         with info^ do begin
  362.             for y := 0 to nLines - 1 do
  363.               for x := 0 to PixelsPerLine - 1 do begin
  364.                 offset := x + y * rowsize;
  365.                 v := (image16^[offset] + round) div one;
  366.                 if v > 255 then
  367.                         v := 255;
  368.                     if v > maxEDM then
  369.                         maxEDM := v;
  370.                     ImageP(PicBaseAddr)^[offset] := v;
  371.               end;
  372.         end;
  373.     end;
  374.  
  375.     procedure SetEdgeValue;
  376.     var
  377.         min, inc, v: LongInt;
  378.         r1, r2, r3, r4, r5, offimage: LongInt;
  379.     begin
  380.         r1 := offset - rowsize - rowsize - 2;
  381.         r2 := r1 + rowsize;
  382.         r3 := r2 + rowsize;
  383.         r4 := r3 + rowsize;
  384.         r5 := r4 + rowsize;
  385.         min := 32767;
  386.           
  387.         offimage := image16^[r3 + 2];
  388.         
  389.         if y < 2 then
  390.             v := offImage + one
  391.         else
  392.             v := image16^[r2 + 2] + one;
  393.         if v < min then
  394.             min := v;
  395.             
  396.         if x < 2 then
  397.             v := offImage + one
  398.         else
  399.             v := image16^[r3 + 1] + one;
  400.         if v < min then
  401.             min := v;
  402.             
  403.         if x > xmax then
  404.             v := offImage + one
  405.         else
  406.             v := image16^[r3 + 3] + one;
  407.         if v < min then
  408.             min := v;
  409.             
  410.         if y > ymax then
  411.             v := offImage + one
  412.         else
  413.             v := image16^[r4 + 2] + one;
  414.         if v < min then
  415.             min := v;
  416.         
  417.         if (x < 2) or (y < 2) then
  418.             v := offImage + sqrt2
  419.         else
  420.             v := image16^[r2 + 1] + sqrt2;
  421.         if v < min then
  422.             min := v;
  423.             
  424.         if (x > xmax) or (y < 2) then
  425.             v := offImage + sqrt2
  426.         else
  427.             v := image16^[r2 + 3] + sqrt2;
  428.         if v < min then
  429.             min := v;
  430.             
  431.         if (x < 2) or (y > ymax) then
  432.             v := offImage + sqrt2
  433.         else
  434.             v := image16^[r4 + 1] + sqrt2;
  435.         if v < min then
  436.             min := v;
  437.             
  438.         if (x > xmax) or (y > ymax) then
  439.             v := offImage + sqrt2
  440.         else
  441.             v := image16^[r4 + 3] + sqrt2;
  442.         if v < min then
  443.             min := v;
  444.         
  445.         if (x < 2) or (y < 2) then
  446.             v := offImage + sqrt5
  447.         else
  448.             v := image16^[r1 + 1] + sqrt5;
  449.         if v < min then
  450.             min := v;
  451.             
  452.         if (x > xmax) or (y < 2) then
  453.             v := offImage + sqrt5
  454.         else
  455.             v := image16^[r1 + 3] + sqrt5;
  456.         if v < min then
  457.             min := v;
  458.             
  459.         if (x > xmax) or (y < 2) then
  460.             v := offImage + sqrt5
  461.         else
  462.             v := image16^[r2 + 4] + sqrt5;
  463.         if v < min then
  464.             min := v;
  465.             
  466.         if (x > xmax) or (y > ymax) then
  467.             v := offImage + sqrt5
  468.         else
  469.             v := image16^[r4 + 4] + sqrt5;
  470.         if v < min then
  471.             min := v;
  472.             
  473.         if (x > xmax) or (y > ymax) then
  474.             v := offImage + sqrt5
  475.         else
  476.             v := image16^[r5 + 3] + sqrt5;
  477.         if v < min then
  478.             min := v;
  479.             
  480.         if (x < 2) or (y > ymax) then
  481.             v := offImage + sqrt5
  482.         else
  483.         v := image16^[r5 + 1] + sqrt5;
  484.         if v < min then
  485.             min := v;
  486.             
  487.         if (x < 2) or (y > ymax) then
  488.             v := offImage + sqrt5
  489.         else
  490.             v := image16^[r4] + sqrt5;
  491.         if v < min then
  492.             min := v;
  493.             
  494.         if (x < 2) or (y < 2) then
  495.             v := offImage + sqrt5
  496.         else
  497.             v := image16^[r2] + sqrt5;
  498.         if v < min then
  499.             min := v;
  500.         
  501.         image16^[offset] := min;
  502.     end; {SetEdgeValue}
  503.  
  504.     procedure SetValue;
  505.     var
  506.         min, inc, v: LongInt;
  507.         r1, r2, r3, r4, r5: LongInt;
  508.     begin
  509.         r1 := offset - rowsize - rowsize - 2;
  510.         r2 := r1 + rowsize;
  511.         r3 := r2 + rowsize;
  512.         r4 := r3 + rowsize;
  513.         r5 := r4 + rowsize;
  514.         min := 32767;
  515.           
  516.         v := image16^[r2 + 2] + one;
  517.         if v < min then
  518.             min := v;
  519.         v := image16^[r3 + 1] + one;
  520.         if v < min then
  521.             min := v;
  522.         v := image16^[r3 + 3] + one;
  523.         if v < min then
  524.             min := v;
  525.         v := image16^[r4 + 2] + one;
  526.         if v < min then
  527.             min := v;
  528.         
  529.         v := image16^[r2 + 1] + sqrt2;
  530.         if v < min then
  531.             min := v;
  532.         v := image16^[r2 + 3] + sqrt2;
  533.         if v < min then
  534.             min := v;
  535.         v := image16^[r4 + 1] + sqrt2;
  536.         if v < min then
  537.             min := v;
  538.         v := image16^[r4 + 3] + sqrt2;
  539.         if v < min then
  540.             min := v;
  541.         
  542.         v := image16^[r1 + 1] + sqrt5;
  543.         if v < min then
  544.             min := v;
  545.         v := image16^[r1 + 3] + sqrt5;
  546.         if v < min then
  547.             min := v;
  548.         v := image16^[r2 + 4] + sqrt5;
  549.         if v < min then
  550.             min := v;
  551.         v := image16^[r4 + 4] + sqrt5;
  552.         if v < min then
  553.             min := v;
  554.         v := image16^[r5 + 3] + sqrt5;
  555.         if v < min then
  556.             min := v;
  557.         v := image16^[r5 + 1] + sqrt5;
  558.         if v < min then
  559.             min := v;
  560.         v := image16^[r4] + sqrt5;
  561.         if v < min then
  562.             min := v;
  563.         v := image16^[r2] + sqrt5;
  564.         if v < min then
  565.             min := v;
  566.         
  567.         image16^[offset] := min;
  568.     end; {SetValue}
  569.     
  570.  
  571.     procedure CheckAbort;
  572.     begin
  573.         ShowAnimatedWatch;
  574.         if CommandPeriod then begin
  575.             beep;
  576.             DisposePtr(ptr(image16));
  577.             exit(MakeEDM);
  578.         end;
  579.         NextTicks := TickCount + 15;
  580.     end;
  581.  
  582.  
  583.     procedure MakeCoordinateArrays;
  584.     {Generates the XY coordinate arrays that allow pixels at each level to
  585.     be accesed directly without searching through the entire image. This
  586.     method, suggested by Stein Roervik (steinr@kjemi.unit.no), greatly
  587.     speeds up the watershed segmentation routine.}
  588.     var
  589.         i, x, y, rowsize, offset, v, ArraySize: LongInt;
  590.         LevelOffset: array[0..255] of LongInt;
  591.         image: ImageP;
  592.     begin
  593.         with info^ do begin
  594.             RoiRect := PicRect;
  595.             GetRectHistogram;
  596.             ArraySize := 0;
  597.             for i := 1 to maxEDM - 1 do
  598.                 ArraySize := ArraySize + histogram[i];
  599.             xCoordinate := Image16Ptr(NewPtr(ArraySize * SizeOf(integer)));
  600.             yCoordinate := Image16Ptr(NewPtr(ArraySize * SizeOf(integer)));
  601.             if (xCoordinate = nil) or (yCoordinate = nil) then begin
  602.                 if xCoordinate <> nil then
  603.                     DisposePtr(ptr(xCoordinate));
  604.                 DisposePtr(ptr(image2));
  605.                 PutError('Out of memory');
  606.                 undo;
  607.                 WhatToUndo := NothingToUndo;
  608.                 exit(MakeEDM);
  609.             end;
  610.             image := ImageP(PicbaseAddr);
  611.             offset := 0;
  612.             for i := 0 to 255 do begin
  613.                 LevelStart[i] := offset;
  614.                 if (i >= 1) and (i <= (MaxEDM - 1)) then
  615.                     offset := offset + histogram[i];
  616.             end;
  617.             for I := 0 to 255 do
  618.                 LevelOffset[i] := 0;
  619.             rowsize := BytesPerRow;
  620.             for y := 0 to nLines - 1 do begin
  621.                 for x := 0 to PixelsPerLine - 1 do begin
  622.                     v := image^[x + y * rowsize];
  623.                     if (v >= 1) and (v <= (MaxEDM - 1)) then begin
  624.                         offset := LevelStart[v] + LevelOffset[v];
  625.                         xCoordinate^[offset] := x;
  626.                         yCoordinate^[offset] := y;
  627.                         LevelOffset[v] := LevelOffset[v] + 1;
  628.                     end;
  629.                 end; {for x}
  630.             end; {for y}
  631.         end; {with}
  632.     end;
  633.  
  634. begin
  635.     with info^ do begin
  636.         ShowWatch;
  637.         KillRoi;
  638.         rowsize := BytesPerRow;
  639.         xmax := PixelsPerLine - 3;
  640.         ymax := nLines - 3;
  641.         StartTicks := TickCount;
  642.         NextTicks := TickCount;
  643.         SetupUndo;
  644.         WhatToUndo := UndoFilter;
  645.         if not ConvertToIntegers then begin
  646.             AbortMacro;
  647.             PutError('Out of Memory');
  648.             exit(MakeEDM);
  649.         end;
  650.         for y := 0 to nLines - 1 do begin
  651.             for x := 0 to PixelsPerLine - 1 do begin
  652.                 offset := x + y * rowsize;
  653.                 if image16^[offset] > 0.0 then begin
  654.                     if (x < 2) or (x > xmax) or (y < 2) or (y > ymax) then
  655.                         SetEdgeValue
  656.                     else
  657.                         SetValue;
  658.                 end;
  659.             end;
  660.             if TickCount > NextTicks then
  661.                 CheckAbort;
  662.         end;
  663.         for y := nLines - 1 downto 0 do begin
  664.             for x := PixelsPerLine - 1 downto 0 do begin
  665.             offset := x + y * rowsize;
  666.                 if image16^[offset] > 0.0 then begin
  667.                     if (x < 2) or (x > xmax) or (y < 2) or (y > ymax) then
  668.                         SetEdgeValue
  669.                     else
  670.                         SetValue;
  671.                 end;
  672.             end;
  673.             if TickCount > NextTicks then
  674.                 CheckAbort;
  675.         end;
  676.         ConvertToBytes;
  677.         DisposePtr(ptr(image16));
  678.         if (item = UltimateItem) or (item = WatershedItem) then begin
  679.             image2 := ImageP(NewPtr(PixMapSize));
  680.             if image2 = nil then
  681.                 exit(MakeEDM);
  682.             if ((item = WatershedItem) and not OptionKeyWasDown) or ((item = UltimateItem) and OptionKeyWasDown) then
  683.                 SmoothEDM(image2);
  684.             MakeCoordinateArrays;
  685.             PointsOK := FindUtimatePoints(MaxEDM, item, image2, LevelStart, xCoordinate, yCoordinate);
  686.         end;
  687.         if (item = WatershedItem) and PointsOK then begin
  688.             DoWatershedSegmentation(MaxEDM, image2, LevelStart, xCoordinate, yCoordinate, iterations);
  689.             str := StringOf(MaxEDM - 1:1, ' levels', crStr, iterations/(MaxEDM - 1):1:2, ' iterations/level');
  690.         end else
  691.             str := '';
  692.         if (item = UltimateItem) or (item = WatershedItem) then begin
  693.             DisposePtr(ptr(xCoordinate));
  694.             DisposePtr(ptr(yCoordinate));
  695.             DisposePtr(ptr(image2));
  696.         end;
  697.         ShowTime(StartTicks, PicRect, str);
  698.         changes := true;
  699.         UpdatePicWindow;
  700.     end; {with}
  701. end;
  702.  
  703. end.